package com.atguigu.gmall.wms.service.impl;

import com.atguigu.gmall.wms.vo.SkuLockVo;
import com.google.gson.Gson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gmall.common.bean.PageResultVo;
import com.atguigu.gmall.common.bean.PageParamVo;

import com.atguigu.gmall.wms.mapper.WareSkuMapper;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.service.WareSkuService;
import org.springframework.util.CollectionUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuMapper, WareSkuEntity> implements WareSkuService {
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    StringRedisTemplate redisTemplate;
    String lockKeyPrefix = "stock:lock:";
    String skuLockVosKeyPrefix = "sku:locks:";
    @Override
    public PageResultVo queryPage(PageParamVo paramVo) {
        IPage<WareSkuEntity> page = this.page(
                paramVo.getPage(),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageResultVo(page);
    }

    @Override
    public List<SkuLockVo> checkAndLock(List<SkuLockVo> skuLockVos) {
        /*
            遍历skuLockVos
            每个skuLockVo ，根据skuId 和要购买的数量count,去wms_ware_sku 表中查询该商品所有的仓库 是否有满足库存要求的仓库对象
                如果查询到的仓库对象不为空:代表验库存成功  否则验库存失败:设置skuLockVo的stock=false并返回
                库存足够：锁定库存(更新wms_ware_sku 表中 验库存成功得到的库存对象的 stock_locked属性值+count )

                验库存和锁库存 为了避免其他请求插队执行导致验库存得到的结果不准确，需要使用锁(分布式锁) 保证原子性操作
                            -- 加锁时 可以使用skuId拼接锁的key，相同skuId的多个锁库存操作才会互斥
         */
        if(CollectionUtils.isEmpty(skuLockVos)){//没有获取到要验证的skuLockvo集合
            return null;
        }
        skuLockVos.forEach(skuLockVo -> {
            //将验库存锁库存的代码编写到独立方法中
            checkLock(skuLockVo);
        });
        // 判断验库存锁库存的结果：
        // 1、如果有验库存锁库存失败的，将 失败的集合返回
        List<SkuLockVo> failSkuLockVos = skuLockVos.stream().filter(skuLockVo -> !skuLockVo.getStock()).collect(Collectors.toList());
        if(!CollectionUtils.isEmpty(failSkuLockVos)){
            /*
                表示验库存锁库存失败了，结束订单的创建
                锁定成功的skuLockvo的库存需要释放
             */
            //获取锁定库存成功的对象
            skuLockVos.stream().filter(skuLockVo -> skuLockVo.getStock()).forEach(skuLockVo -> {
                baseMapper.unlockStock(skuLockVo.getWareSkuId() , skuLockVo.getCount());
            });
            return failSkuLockVos;
        }
        //2、如果全部成功返回null;
        //以后有多个场景都需要释放库存，为了方便获取释放库存的信息
        redisTemplate.opsForValue().set(skuLockVosKeyPrefix+ skuLockVos.get(0).getOrderToken(),
                gson.toJson(skuLockVos));

        return null;
    }
    Gson gson = new Gson();
    //验库存锁库存：保证原子性操作   使用分布式 公平锁
    private void checkLock(SkuLockVo skuLockVo) {
        // 每个skuId都有自己的分布式锁
        RLock fairLock = redissonClient.getFairLock(lockKeyPrefix + skuLockVo.getSkuId());
        try {
            fairLock.lock();
            //验库存
            List<WareSkuEntity> wareSkuEntities = baseMapper.checkStock(skuLockVo.getSkuId(), skuLockVo.getCount());
            if(CollectionUtils.isEmpty(wareSkuEntities)){
                //skuLockVo对应的sku  没有库存
                skuLockVo.setStock(false);
                return ;
            }
            //有库存 锁库存
            // 锁库存查找仓库时  会根据提交订单用户选择的收货地址 匹配城市区域id 匹配合适的仓库id
            // 使用第一个仓库锁定库存
            WareSkuEntity wareSkuEntity = wareSkuEntities.get(0);
            if(baseMapper.lockStock(wareSkuEntity.getId() , skuLockVo.getCount())){
                //锁库存成功
                skuLockVo.setWareSkuId(wareSkuEntity.getId());
                skuLockVo.setStock(true);
            }else{
                //锁库存失败
                skuLockVo.setStock(false);
            }

        } finally {
            //必须释放锁
            fairLock.unlock();
        }
    }

}